//如果你想在用户态动态查看驱动状态、统计信息、内部变量 —— 不要用 sysfs，请用 debugfs。
// 所在目录 /sys/kernel/debug

//mount -t debugfs none /sys/kernel/debug

// grep CONFIG_DEBUG_FS /boot/config-$(uname -r) :确认内核编译时启用了 CONFIG_DEBUG_FS 选项

//动态监控 debugfs 操作
// echo 'file *debugfs* +p' > /sys/kernel/debug/dynamic_debug/control

/*
pr_debug 动态调试版本,不会输出
//要启用该机制，需内核编译时打开 CONFIG_DYNAMIC_DEBUG=y.这时 pr_ 函数不会输出
// 查看该文件中 pr_debug 的输出
echo 'file debugfs_test.c +p' > /sys/kernel/debug/dynamic_debug/control
// 关闭
echo 'file debugfs_test.c -p' > /sys/kernel/debug/dynamic_debug/control
// 或 modprobe my_driver dyndbg=+p
*/

/*
//通过 strace 监控系统调用
strace -f -o debugfs.log insmod your_module.ko
grep debugfs_create debugfs.log
*/

/*
//启用内核函数跟踪 ftrace
echo function > /sys/kernel/debug/tracing/current_tracer
echo 'debugfs_*' > /sys/kernel/debug/tracing/set_ftrace_filter
echo 'vfs_*' >> /sys/kernel/debug/tracing/set_ftrace_filter
echo 1 > /sys/kernel/debug/tracing/tracing_on
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace | grep -E 'debugfs_create_dir|d_instantiate|security_inode_create'
*/

/*
trace_printk：开发期临时追踪利器
trace_printk 用于快速插桩，但不会污染 dmesg
使用:
#include <linux/tracepoint.h>
trace_printk("trace_printk test\n");

echo 1 > /sys/kernel/debug/tracing/tracing_on
insmod test.ko
cat /sys/kernel/debug/tracing/trace | grep "trace_printk test"
echo 0 > /sys/kernel/debug/tracing/tracing_on
echo > /sys/kernel/debug/tracing/trace

查看:
cat /sys/kernel/debug/tracing/trace
开启：
echo 1 > /sys/kernel/debug/tracing/tracing_on
关闭:
echo 0 > /sys/kernel/debug/tracing/tracing_on
清除:
echo > /sys/kernel/debug/tracing/trace
备份:
cp /sys/kernel/debug/tracing/trace ~/trace_backup.log
*/

// #define DEBUG

// 为 pr_XXX 打印函数增加模块名
// #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/cpu.h>
//开发期临时追踪利器
#include <linux/tracepoint.h>

struct test_debugfs_driver {
    const char *name;
    //要统计的信息
    u32 irq_count;
    long data;
#ifdef CONFIG_DEBUG_FS
    struct mutex data_mutex;
    //要创建的目录,然后在其中创建自己的统计信息文件
    struct dentry *dir;
    struct dentry *data_file;
#endif

};

static struct test_debugfs_driver *test_drv;

#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>

#define MY_OPS
#ifdef MY_OPS
static ssize_t data_get(struct file *file, char __user *buf, size_t len, loff_t *ppos) {
    char tmp[32];
    int ret = snprintf(tmp, sizeof(tmp), "%ld\n", test_drv->data);

    pr_warn("get data=%lu\n",test_drv->data);

    return simple_read_from_buffer(buf, len, ppos, tmp, ret);
}

static ssize_t data_set(struct file *filp, const char __user *buf,
                           size_t len, loff_t *ppos) {
    char tmp[32];
    if (len >= sizeof(tmp)) return -EINVAL;

	/* No CPUs should come up or down during this. */
	cpus_read_lock();
	mutex_lock(&test_drv->data_mutex);

    if (copy_from_user(tmp, buf, len)){
	    mutex_unlock(&test_drv->data_mutex);
        cpus_read_unlock();
        return -EFAULT;
    }
    tmp[len] = '\0';
    if (kstrtol(tmp, 10, &test_drv->data)) {  // 字符串转int
	    mutex_unlock(&test_drv->data_mutex);
        cpus_read_unlock();
        return -EINVAL;
    }
    pr_warn("set data=%ld\n",test_drv->data);

	mutex_unlock(&test_drv->data_mutex);
    cpus_read_unlock();

    return len;
}

// 文件操作结构体
static const struct file_operations data_fops = {
    .owner = THIS_MODULE,
    .read = data_get,
    .write = data_set,
};

#else

static int data_set(void *data, u64 val)
{
	/* No CPUs should come up or down during this. */
	cpus_read_lock();
	mutex_lock(&test_drv->data_mutex);

    test_drv->data = (long)val;
    pr_warn("set data=%llu\n",test_drv->data);

	mutex_unlock(&test_drv->data_mutex);
    cpus_read_unlock();

	return 0;
}

static int data_get(void *data, u64 *val)
{
    *val = (u64)test_drv->data;
    pr_warn("get data=%llu\n",*val);
	return 0;
}

DEFINE_DEBUGFS_ATTRIBUTE(data_fops, data_get, data_set, "%llu\n");

#endif
static void test_debug_create(struct test_debugfs_driver *tdd)
{
    // 模块初始化时创建目录（位于 /sys/kernel/debug/ 下）
    tdd->dir = debugfs_create_dir(tdd->name,NULL);
    if (IS_ERR_OR_NULL(tdd->dir)){
        pr_err("create dir err\n");
		goto failed_debugfs_dir;
    }

    pr_info("create dir '%s' sucess\n", tdd->name);
    
    // 在目录下创建文件
    tdd->data_file = debugfs_create_file("data", 0644, tdd->dir, NULL, &data_fops);
    if (IS_ERR_OR_NULL(tdd->data_file)){
        pr_err("create file err\n");
		goto failed_debugfs_data;
    }

    pr_info("create file 'data' sucess\n");

    debugfs_create_u32("irq_count",0444,tdd->dir,&tdd->irq_count);

    return;

failed_debugfs_data:
	debugfs_remove(tdd->dir);
    tdd->data_file = NULL;
failed_debugfs_dir:
	tdd->dir = NULL;
}

static void test_debug_destroy(struct test_debugfs_driver *tdd)
{
	if (tdd->dir) {
        pr_info("destroy dir '%s' addr: %px\n", tdd->name,tdd->dir);
        //递归删除指定目录及其​​所有子目录和文件
        debugfs_remove_recursive(tdd->dir);
        tdd->dir = NULL;
    }
    pr_info("destroy end\n");
}

#endif

// static DEFINE_MUTEX(debug_mutex);  // 定义全局互斥锁

static int __init test_debugfs_init(void){
    pr_info("init\n");

	test_drv = kzalloc(sizeof(*test_drv), GFP_KERNEL);
    if (!test_drv) return -ENOMEM;

    test_drv->name = "test_driver";

#ifdef CONFIG_DEBUG_FS
    mutex_init(&test_drv->data_mutex);
    test_debug_create(test_drv);
#endif
    pr_info("init end\n");

    //强大的开发期工具，用于快速插桩，但不会污染 dmesg
    trace_printk("trace_printk test\n");

    return 0;
}

static void __exit test_debugfs_exit(void){
    pr_info("exit\n");

#ifdef CONFIG_DEBUG_FS
    test_debug_destroy(test_drv);
    mutex_destroy(&test_drv->data_mutex);
#endif
	kfree(test_drv);
    pr_info("exit end\n");
}

module_init(test_debugfs_init);
module_exit(test_debugfs_exit);

MODULE_LICENSE("GPL");

